package com.futurepress.staticserver;

import android.support.annotation.Nullable;
import android.util.Log;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReadableMapKeySetIterator;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.UUID;

import fi.iki.elonen.NanoHTTPD;
import fi.iki.elonen.SimpleWebServer;



public class WebServer extends SimpleWebServer
{
    private static final String TAG = "WebServer";
    private ReactApplicationContext context;

    public WebServer(ReactApplicationContext context, String localAddr, int port, File wwwroot) throws IOException {
        super(localAddr, port, wwwroot, true, "*");

        mimeTypes().put("xhtml", "application/xhtml+xml");
        mimeTypes().put("opf", "application/oebps-package+xml");
        mimeTypes().put("ncx", "application/xml");
        mimeTypes().put("epub", "application/epub+zip");
        mimeTypes().put("otf", "application/x-font-otf");
        mimeTypes().put("ttf", "application/x-font-ttf");
        mimeTypes().put("js", "application/javascript");
        mimeTypes().put("svg", "image/svg+xml");

        this.responses = new HashMap<String, Object>();
        this.context = context;
    }

    public HashMap<String, Object> responses;

    public void addResponse(String requestId, ReadableMap response){
        this.responses.put(requestId, response);
    }

    private String getBodyText(IHTTPSession session) {
        Map<String, String> files = new HashMap<String, String>();
        Method method = session.getMethod();
        if (Method.PUT.equals(method) || Method.POST.equals(method)) {
            try {
                session.parseBody(files);
            } catch (IOException ioe) {
                return "{}";
            } catch (ResponseException re) {
                return "{}";
            }
        }
        if (Method.PUT.equals(method) ) {
            // get the POST body
            return files.get("content");
        } else if (Method.POST.equals(method)) {
            // get the POST body
            return files.get("postData");
        } else {
            return "{}";
        }
    }

    /**
     * Create a request object
     * <p>
     * [
     * "requestId": requestUUID,
     * "      body": request.jsonObject ?? "",
     * "      headers": request.headers,
     * "      method": request.method,
     * "      path": request.url.path,
     * "      query": request.url.query ?? ""
     * ]
     *
     * @param session
     * @return
     */
    private JSONObject createJSONRequest(String requestId, IHTTPSession session) throws JSONException {
        JSONObject jsonRequest = new JSONObject();
        jsonRequest.put("requestId", requestId);
        jsonRequest.put("body", this.getBodyText(session));
        jsonRequest.put("headers", session.getHeaders());
        jsonRequest.put("method", session.getMethod());
        jsonRequest.put("path", session.getUri());
        jsonRequest.put("query", session.getQueryParameterString());
        return jsonRequest;
    }

    private WritableMap createRequest(String requestId, IHTTPSession session) {
        WritableMap request = Arguments.createMap();
        request.putString("requestId", requestId);
        request.putString("body", this.getBodyText(session));
        request.putMap("headers", getFromMap(session.getHeaders()));
        request.putString("method", session.getMethod().name());
        request.putString("path", session.getUri());
        request.putString("query", session.getQueryParameterString());
        return request;
    }

    private WritableMap getFromMap(Map<String, String> headers) {
        WritableMap headerMap = Arguments.createMap();
        Iterator<String> keyIterator = headers.keySet().iterator();
        while (keyIterator.hasNext()){
            String key = keyIterator.next();
            headerMap.putString(key, headers.get(key));
        }
        return headerMap;
    }

    private String getContentType(ReadableMap responseObject) throws JSONException {
        if (responseObject.hasKey("headers") &&
                responseObject.getMap("headers").hasKey("Content-Type")) {
            return responseObject.getMap("headers").getString("Content-Type");
        } else {
            return "text/plain";
        }
    }

    public void sendEvent(String eventName, @Nullable WritableMap params){
        // Log.w(TAG, "send Event:"+params.toString());
        this.context.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(eventName, params);
    }
    public void sendEvent(String eventName, @Nullable String params){
        // Log.w(TAG, "send Event:"+params);
        this.context.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(eventName, params);
    }

    @Override
    public Response serve(IHTTPSession session){
        String uri = session.getUri();
        //Log.d(TAG,"on serve Request from:"+session.getUri());
        if (uri.startsWith("/api")){
            //Log.d(TAG, "on serve Request start with api");
            String requestUUID = UUID.randomUUID().toString();
            try {
                // JSONObject request= this.createJSONRequest(requestUUID, session);
                // this.sendEvent("request", request.toString());
                // Log.d(TAG, "on serve Request start with api");
                this.sendEvent("request", this.createRequest(requestUUID, session));
            } catch (Exception e) {
                e.printStackTrace();
            }
            while (!this.responses.containsKey(requestUUID)) {
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            ReadableMap responseObject = (ReadableMap) this.responses.get(requestUUID);
            Response response = null;
            Log.d(TAG, "responseObject: " + responseObject.toString());
            try {
                response = NanoHTTPD.newFixedLengthResponse(
                        Response.Status.lookup(responseObject.getInt("status")),
                        getContentType(responseObject),
                        responseObject.getString("body")
                );

                ReadableMapKeySetIterator keys = responseObject.getMap("headers").keySetIterator();
                while (keys.hasNextKey()) {
                    String key = (String) keys.nextKey();
                    response.addHeader(
                            key,
                            responseObject.getMap("headers").getString(key)
                    );
                }

            } catch (JSONException e) {
                e.printStackTrace();
            }
            this.responses.remove(requestUUID);
            return response;
        }
        return super.serve(session);
    }

    @Override
    protected boolean useGzipWhenAccepted(Response r) {
        return super.useGzipWhenAccepted(r) && r.getStatus() != Response.Status.NOT_MODIFIED;
    }
}
